package passwordcheckerrefactored;

public class PasswordCheckerByTodnem implements PasswordChecker {

    @Override
    public String check(String password) {
	// Simultaneous variable declaration and value assignment aren't supported in IE apparently
	// so I'm forced to assign the same value individually per var to support a crappy browser *sigh*
	int nScore=0, nLength=0, nAlphaUC=0, nAlphaLC=0, nNumber=0, nSymbol=0, nMidChar=0, nRequirements=0, nAlphasOnly=0, nNumbersOnly=0, nUnqChar=0, nRepChar=0, nConsecAlphaUC=0, nConsecAlphaLC=0, nConsecNumber=0, nConsecSymbol=0, nConsecCharType=0, nSeqAlpha=0, nSeqNumber=0, nSeqSymbol=0, nSeqChar=0, nReqChar=0, nMultConsecCharType=0;
	double nRepInc=0.0;
        int nMultRepChar=1, nMultConsecSymbol=1;
	int nMultMidChar=2, nMultRequirements=2, nMultConsecAlphaUC=2, nMultConsecAlphaLC=2, nMultConsecNumber=2;
	int nReqCharType=3, nMultAlphaUC=3, nMultAlphaLC=3, nMultSeqAlpha=3, nMultSeqNumber=3, nMultSeqSymbol=3;
	int nMultLength=4, nMultNumber=4;
	int nMultSymbol=6;
	int nTmpAlphaUC=-1, nTmpAlphaLC=-1, nTmpNumber=-1, nTmpSymbol=-1;
	String sAlphaUC="0", sAlphaLC="0", sNumber="0", sSymbol="0", sMidChar="0", sRequirements="0",
                sAlphasOnly="0", sNumbersOnly="0", sRepChar="0", sConsecAlphaUC="0", sConsecAlphaLC="0",
                sConsecNumber="0", sSeqAlpha="0", sSeqNumber="0", sSeqSymbol="0";
	String sAlphas = "abcdefghijklmnopqrstuvwxyz";
	String sNumerics = "01234567890";
	String sSymbols = ")!@#$%^&*()";
	String sComplexity = "Too Short";
	String sStandards = "Below";
	int nMinPwdLen = 8;
	if (password != null && !password.equals("")) {
            nScore = password.length() * nMultLength;
            nLength = password.length();
            char[] pwd2 = password.replaceAll("\\s+", "").toCharArray();
            int arrPwdLen = pwd2.length;
            /* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */
            for (int i = 0; i < arrPwdLen; i++) {
                if (Character.isLetter(pwd2[i]) && Character.isUpperCase(pwd2[i])) {
                    if (nTmpAlphaUC != -1) { if ((nTmpAlphaUC + 1) == i) { nConsecAlphaUC++; nConsecCharType++; } }
                    nTmpAlphaUC = i;
                    nAlphaUC++;
                }
                else if (Character.isLetter(pwd2[i]) && Character.isLowerCase(pwd2[i])) {
                    if (nTmpAlphaLC != -1) { if ((nTmpAlphaLC + 1) == i) { nConsecAlphaLC++; nConsecCharType++; } }
                    nTmpAlphaLC = i;
                    nAlphaLC++;
		}
                else if (Character.isDigit(pwd2[i])) {
                    if (i > 0 && i < (arrPwdLen - 1)) { nMidChar++; }
                        if (nTmpNumber != -1) { if ((nTmpNumber + 1) == i) { nConsecNumber++; nConsecCharType++; } }
                            nTmpNumber = i;
                            nNumber++;
			}
			else if (!Character.isDigit(pwd2[i]) && !Character.isLetter(pwd2[i]) && pwd2[i] != '_') {
                            if (i > 0 && i < (arrPwdLen - 1)) { nMidChar++; }
                            if (nTmpSymbol != -1) { if ((nTmpSymbol + 1) == i) { nConsecSymbol++; nConsecCharType++; } }
                            nTmpSymbol = i;
                            nSymbol++;
			}
			/* Internal loop through password to check for repeat characters */
			boolean bCharExists = false;
			for (int b=0; b < arrPwdLen; b++) {
                            if (pwd2[i] == pwd2[b] && i != b) { /* repeat character exists */
                                bCharExists = true;
                                /*
                                Calculate icrement deduction based on proximity to identical characters
                                Deduction is incremented each time a new match is discovered
                                Deduction amount is based on total password length divided by the
                                difference of distance between currently selected match
                                */
                                nRepInc += Math.abs((double)arrPwdLen/(b-i));
				}
			}
			if (bCharExists) {
				nRepChar++;
				nUnqChar = arrPwdLen-nRepChar;
				nRepInc = (nUnqChar != 0) ? (int)Math.ceil(nRepInc/nUnqChar) : (int)Math.ceil(nRepInc);
			}
		}

		/* Check for sequential alpha string patterns (forward and reverse) */
		for (int s=0; s < 23; s++) {
			String sFwd = sAlphas.substring(s,s+3);
			String sRev = new StringBuffer(sFwd).reverse().toString();
			if (password.toLowerCase().indexOf(sFwd) != -1 || password.toLowerCase().indexOf(sRev) != -1) { nSeqAlpha++; nSeqChar++;}
		}

		/* Check for sequential numeric string patterns (forward and reverse) */
		for (int s=0; s < 8; s++) {
			String sFwd = sNumerics.substring(s,s+3);
			String sRev = new StringBuffer(sFwd).reverse().toString();
			if (password.toLowerCase().indexOf(sFwd) != -1 || password.toLowerCase().indexOf(sRev) != -1) { nSeqNumber++; nSeqChar++;}
		}

		/* Check for sequential symbol string patterns (forward and reverse) */
		for (int s=0; s < 8; s++) {
			String sFwd = sSymbols.substring(s,s+3);
			String sRev = new StringBuffer(sFwd).reverse().toString();
			if (password.toLowerCase().indexOf(sFwd) != -1 || password.toLowerCase().indexOf(sRev) != -1) { nSeqSymbol++; nSeqChar++;}
		}

	/* Modify overall score value based on usage vs requirements */

		/* General point assignment */
		System.out.println("nLengthBonus is: + " + nScore);
		if (nAlphaUC > 0 && nAlphaUC < nLength) {
			nScore = nScore + (nLength - nAlphaUC) * 2;
			sAlphaUC = "+ " + (nLength - nAlphaUC) * 2;
		}
		if (nAlphaLC > 0 && nAlphaLC < nLength) {
			nScore = nScore + (nLength - nAlphaLC) * 2;
			sAlphaLC = "+ " + (nLength - nAlphaLC) * 2;
		}
		if (nNumber > 0 && nNumber < nLength) {
			nScore = nScore + nNumber * nMultNumber;
			sNumber = "+ " + nNumber * nMultNumber;
		}
		if (nSymbol > 0) {
			nScore = nScore + nSymbol * nMultSymbol;
			sSymbol = "+ " + nSymbol * nMultSymbol;
		}
		if (nMidChar > 0) {
			nScore = nScore + nMidChar * nMultMidChar;
			sMidChar = "+ " + nMidChar * nMultMidChar;
		}
		System.out.println("nAlphaUCBonus is: " + sAlphaUC);
                System.out.println("nAlphaLCBonus is: " + sAlphaLC);
                System.out.println("nNumberBonus is: " + sNumber);
                System.out.println("nSymbolBonus is: " + sSymbol);
                System.out.println("nMidCharBonus is: " + sMidChar);

		/* Point deductions for poor practices */
		if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol == 0 && nNumber == 0) {  // Only Letters
			nScore = nScore - nLength;
			nAlphasOnly = nLength;
			sAlphasOnly = "- " + nLength;
		}
		if (nAlphaLC == 0 && nAlphaUC == 0 && nSymbol == 0 && nNumber > 0) {  // Only Numbers
			nScore = nScore - nLength;
			nNumbersOnly = nLength;
			sNumbersOnly = "- " + nLength;
		}
		if (nRepChar > 0) {  // Same character exists more than once
			nScore = nScore - (int)nRepInc;
			sRepChar = "- " + nRepInc;
		}
		if (nConsecAlphaUC > 0) {  // Consecutive Uppercase Letters exist
			nScore = nScore - nConsecAlphaUC * nMultConsecAlphaUC;
			sConsecAlphaUC = "- " + nConsecAlphaUC * nMultConsecAlphaUC;
		}
		if (nConsecAlphaLC > 0) {  // Consecutive Lowercase Letters exist
			nScore = nScore - nConsecAlphaLC * nMultConsecAlphaLC;
			sConsecAlphaLC = "- " + nConsecAlphaLC * nMultConsecAlphaLC;
		}
		if (nConsecNumber > 0) {  // Consecutive Numbers exist
			nScore = nScore - nConsecNumber * nMultConsecNumber;
			sConsecNumber = "- " + nConsecNumber * nMultConsecNumber;
		}
		if (nSeqAlpha > 0) {  // Sequential alpha strings exist (3 characters or more)
			nScore = nScore - nSeqAlpha * nMultSeqAlpha;
			sSeqAlpha = "- " + nSeqAlpha * nMultSeqAlpha;
		}
		if (nSeqNumber > 0) {  // Sequential numeric strings exist (3 characters or more)
			nScore = nScore - nSeqNumber * nMultSeqNumber;
			sSeqNumber = "- " + nSeqNumber * nMultSeqNumber;
		}
		if (nSeqSymbol > 0) {  // Sequential symbol strings exist (3 characters or more)
			nScore = nScore - nSeqSymbol * nMultSeqSymbol;
			sSeqSymbol = "- " + nSeqSymbol * nMultSeqSymbol;
		}
		System.out.println("nAlphasOnlyBonus is: " + sAlphasOnly);
                System.out.println("nNumbersOnlyBonus is: " + sNumbersOnly);
                System.out.println("sRepCharBonus is: " + sRepChar);
                System.out.println("nConsecAlphaUCBonus is: " + sConsecAlphaUC);
                System.out.println("nConsecAlphaLCBonus is: " + sConsecAlphaLC);
                System.out.println("nConsecNumberBonus is: " + sConsecNumber);
                System.out.println("nSeqAlphaBonus is: " + sSeqAlpha);
                System.out.println("nSeqNumberBonus is: " + sSeqNumber);
                System.out.println("nSeqSymbolBonus is: " + sSeqSymbol);

		/* Determine if mandatory requirements have been met and set image indicators accordingly */
		int[] arrChars = { nLength, nAlphaUC, nAlphaLC, nNumber, nSymbol };
		String[] arrCharsIds = { "nLength", "nAlphaUC", "nAlphaLC", "nNumber", "nSymbol" };
		int arrCharsLen = arrChars.length;
		for (int c=0; c < arrCharsLen; c++) {
                        int minVal;
			if (arrCharsIds[c].equals("nLength")) { minVal = nMinPwdLen - 1; } else { minVal = 0; }
			if (arrChars[c] == (minVal + 1)) { nReqChar++; }
			else if (arrChars[c] > (minVal + 1)) { nReqChar++; }
			else { }
		}
		nRequirements = nReqChar;
                int nMinReqChars;
		if (password.length() >= nMinPwdLen) { nMinReqChars = 3; } else { nMinReqChars = 4; }
		if (nRequirements > nMinReqChars) {  // One or more required characters exist
			nScore = nScore + nRequirements * 2;
			sRequirements = "+ " + nRequirements * 2;
		}
                System.out.println("nRequirementsBonus is: " + sRequirements);

		/* Determine if additional bonuses need to be applied and set image indicators accordingly */
		int[] arrChars2 = { nMidChar, nRequirements };
		String[] arrCharsIds2 = { "nMidChar", "nRequirements" };
		int arrCharsLen2 = arrChars2.length;
		for (int c=0; c < arrCharsLen2; c++) {
                        int minVal;
			if (arrCharsIds2[c].equals("nRequirements")) { minVal = nMinReqChars; } else { minVal = 0; }
			if (arrChars2[c] == (minVal + 1)) { }
			else if (arrChars2[c] > (minVal + 1)) { }
			else { }
		}

		/* Determine if suggested requirements have been met and set image indicators accordingly */
		int[] arrChars3 = {nAlphasOnly,nNumbersOnly,nRepChar,nConsecAlphaUC,nConsecAlphaLC,nConsecNumber,nSeqAlpha,nSeqNumber,nSeqSymbol};
		String[] arrCharsIds3 = {"nAlphasOnly","nNumbersOnly","nRepChar","nConsecAlphaUC","nConsecAlphaLC","nConsecNumber","nSeqAlpha","nSeqNumber","nSeqSymbol"};
		int arrCharsLen3 = arrChars3.length;
		for (int c=0; c < arrCharsLen3; c++) {
			if (arrChars3[c] > 0) { }
			else { }
		}

		/* Determine complexity based on overall score */
		if (nScore > 100) { nScore = 100; } else if (nScore < 0) { nScore = 0; }
		if (nScore >= 0 && nScore < 20) { sComplexity = "Very Weak"; }
		else if (nScore >= 20 && nScore < 40) { sComplexity = "Weak"; }
		else if (nScore >= 40 && nScore < 60) { sComplexity = "Good"; }
		else if (nScore >= 60 && nScore < 80) { sComplexity = "Strong"; }
		else if (nScore >= 80 && nScore <= 100) { sComplexity = "Very Strong"; }

		/* Display updated score criteria to client */
		System.out.println("Score is: " + nScore + "%");
		System.out.println("Complexity is: " + sComplexity);
	}
	else {
		/* Display default score criteria to client */
		System.out.println("Score is: " + nScore + "%");
		System.out.println("Complexity is: " + sComplexity);
        }

        return sComplexity;
    }
}